<?php


namespace app\common\cachemodel;


use app\common\lib\SensorsData;
use app\common\lib\Util;
use app\common\model\BaseCache;
use app\common\model\MessageCenter;
use app\common\model\UserPush;
use think\Exception;
use think\Log;
use traits\model\SoftDelete;
use XznsTools\cache\CacheManage;

class ZlUserVip extends BaseCache
{
    use SoftDelete;

    protected $table = 'wechat_backup_07_18_14_43_00.zl_user_vip';
    public $cacheModel = 'user.user_vip';
    protected $deleteTime = 'delete_time';

    /**
     * 新增用户vip数据
     * @param $map
     * @return bool
     */
    public function SaveData($map)
    {
        try
        {
            $this->save($map);
        }
        catch (\Exception $e)
        {
            return false;
        }
        return true;
    }

    /**
     * 重写数据时获取数据
     * @param $ids
     * @param $type
     * @param $sortType
     * @return array
     */
    public function getInfoByIdsInData($ids, $type, $sortType)
    {
        try
        {
            $data = $this->whereIn('uid', $ids)->column('star_num,vip_level,is_valid,end_time,id,apple_status', 'uid');
        }
        catch (Exception $e)
        {
            return [];
        }

        return $data;
    }

    public function batchGetInfoByIds($cids, $field = [])
    {
        $resData = parent::batchGetInfoByIds($cids, $field);

        return $this->dataInit($cids, $resData);
    }

    /**
     * 数据初始化
     * @param $cids
     * @param array $data
     * @return array
     * @throws \Exception
     */
    public function dataInit($cids, array $data)
    {
        if ($data)
        {
            foreach ($data as $index => $itemData)
            {
                $itemData = $this->innerVipReset($index, $itemData);
                $data[$index] = arrayKeyData2int($itemData, ['star_num', 'vip_level', 'uid', 'is_valid', 'end_time', 'id', 'apple_status', 'update_time']);
            }
        } else
        {
            $cids = is_array($cids) ? $cids : explode(',', $cids);
            foreach ($cids as $cid)
            {
                $res = $this->innerVipReset($cid, []);
                if ($res)
                {
                    $data[$cid] = $res;
                }
            }
        }
        return $data;
    }

    /**
     *  获取用户VIP等级 失效时等级为0
     * @param $uid
     * @return int
     * @throws \Exception
     */
    public function getUserVipLevel($uid): int
    {
        $info = $this->getInfoById($uid);

        $vipLevel = ($info && isset($info['is_valid']) && $info['is_valid'] == 1) ? (int)$info['vip_level'] : 0;

        return $vipLevel;
    }

    /**
     * 更新用户vip数据
     * @param $uid
     * @param $map
     * @return bool
     * @throws \Exception
     */
    public function UpdateData($uid, $map)
    {
        try
        {
            $this->where(['uid' => $uid])->update($map);
        }
        catch (\Exception $e)
        {
            Log::write('ZlUserVip model => ()异常：' . $e->getMessage() . '失败信息：' . $e->getFile() . ':' . $e->getLine() . 'uid:' . $uid, 'error', true);
            return false;
        }
        CacheManage::getInstance()->setCacheConfig($this->cacheModel, $uid)->setCache($map);
        return true;
    }

    /**
     * 缓存获取用户vip信息
     * @param $uid
     * @return array|bool|mixed
     * @throws \Exception
     */
    public function getUserVipFromCache($uid)
    {
        $res = CacheManage::getInstance()->setCacheConfig($this->cacheModel, $uid)->getDataAndSet(function () use ($uid)
        {
            return $this->getUserVipFromDms($uid);
        }, ['star_num', 'vip_level', 'is_valid', 'end_time', 'apple_status']);

        return $this->innerVipReset($uid, $res);
    }

    /**
     * 数据库获取用户vip信息
     * @param $uid
     * @return array
     */
    public function getUserVipFromDms($uid)
    {
        $field = 'id,star_num,vip_level,is_valid,end_time,apple_status';
        try
        {
            $data = $this->where('uid', $uid)->field($field)->findOrFail()->toArray();
        }
        catch (\Exception $e)
        {
            $data = [];
        }
        return $data;
    }

    /**
     * 任务执行确认 是否还能执行
     * true 可执行 false 不能执行
     * @param int $uid
     * @param $type
     * @return bool
     * @throws \Exception
     */
    protected function taskCheck(int $uid, $type): bool
    {
        $starNumRecordModel = new ZlUserStarNumRecord();
        switch ($type)
        {
            case 9: // 每日登陆
            case 14: // 会员到期
                $sucData = $starNumRecordModel->getTaskSuccessInfo($uid);
                if ($sucData[$type] > 0)
                {
                    return false;
                } else
                {
                    return true;
                }
                break;
            case 13: // 绑定手机号
                $num = $starNumRecordModel->where('uid', $uid)->where('type', 13)->count();
                return $num > 0 ? false : true;
                break;
            default:
                return true;
        }
    }

    /**
     * 更新星力值
     * @param $uid
     * @param $type 1=>校准生辰 2=>日运 3=>月运 4=>年运 5=>幸运之城 6=>教学视频 7=>本命预测 8会员购买 9每日登陆 10每日幸运星 13绑定手机 14会员到期 15退款
     * @param int $price
     * @param int $gid 商品ID
     * @param bool $isSend 是否升级自动赠送权益
     * @throws \think\exception\PDOException
     * @throws \Exception
     */
    public function updateStarNum($uid, $type, $price = 0, int $gid = 0, bool $isSend = true)
    {
        // 1=>校准生辰 2=>日运 3=>月运 4=>年运 5=>幸运之城 6=>教学视频 7=>本命预测 8=>开通会员 9每日登陆 10每日幸运星 13绑定手机 14会员到期
        $userVip = $this->getInfoById($uid);
        if (!isset($userVip['id']))
        {
            return;
        }
        if (($userVip && $userVip['is_valid'] == 1 && in_array($type, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 13, 15])) || ($type == 14 && $userVip && $userVip['is_valid'] == 0 && $userVip['star_num'] > 0))
        {   // 是VIP 并且生效中 type != 14 或者 曾经是VIP、会员到期、星力值大于0且type = 14
            if (!$this->taskCheck($uid, $type)) return;// 判断任务是否还能执行

            $vipModel = new ZlVip();
            $starNumRecordModel = new ZlUserStarNumRecord();
            $vipLevel = $vipModel->getInfoById($userVip['vip_level']); // 获取VIP信息

            $taskInfo = config('vip_task.' . $type); // 获取任务信息
            if ($taskInfo)
            {
                if ($type == 15)
                {
                    $level = $userVip['vip_level'];
                    $newStartNum = $vipLevel['star_num'];
                    $starNum = ($userVip['star_num'] - $vipLevel['star_num']) * -1;
                    $flag = 2; // 1收入 2支出
                    $num = $starNumRecordModel->getStarNumMonthlyLimit($uid);
                    $monthStarNumTotal = $num + $starNum;
                } else
                {
                    switch ($taskInfo['type'])
                    {
                        case 1: // 固定星力值
                            $starNum = $taskInfo['min'];
                            break;
                        case 2: // 随机星力值
                            $starNum = random_int($taskInfo['min'], $taskInfo['max']);
                            break;
                        case 3: // 支付价格倍数星力值
                            $starNum = $price * $taskInfo['max'];
                            $starNum = $starNum >= $taskInfo['min'] ? $starNum : $taskInfo['min'];
                            break;
                        case 4: // 商品固定星力值
                            $starNum = $taskInfo['gidAdd'][$gid];
                            break;
                        case 5: // 成长倍数星力值
                            $starNum = $taskInfo['min'] * $vipLevel['grow_num'];
                            break;
                        default:
                            $starNum = 0;
                            break;
                    }

                    $flag = $starNum > 0 ? 1 : 2; // 1收入 2支出
                    $starNum = ceil($starNum);
                    if ($starNum === 0) return;

                    if ($starNum < 0)
                    {   // 支出
                        $newStartNum = $starNum + $userVip['star_num'];

                        $num = $starNumRecordModel->getStarNumMonthlyLimit($uid);
                        $monthStarNumTotal = $num + $starNum;

                        if ($newStartNum < 0)
                        {   // 如果最终的星力值 小于0
                            $newStartNum = 0;
                            $starNum = $userVip['star_num'] * -1;
                        }

                        $level = ($newStartNum < $vipLevel['star_num'] && $userVip['vip_level'] > 1) ? $userVip['vip_level'] - 1 : $userVip['vip_level'];
                    } else
                    {   // 收入
                        $num = $starNumRecordModel->getStarNumMonthlyLimit($uid);
                        if ($flag == 1 && $num >= 5000)
                        {   // 收入并且本月获得星力值达到5000
                            $monthStarNumTotal = $num;
                            $newStartNum = $userVip['star_num'];
                            $starNum = 0;
                            $level = $userVip['vip_level'];
                        } else
                        {   // 本月星力值未达到5000
                            $monthStarNumTotal = $num + $starNum;
                            if ($monthStarNumTotal > 5000)
                            {   // 限制每月获取星力值上线5000
                                $monthStarNumTotal = 5000;
                                $starNum = 5000 - $num;
                            }

                            $newStartNum = $starNum + $userVip['star_num'];

                            if ($userVip['vip_level'] < 8)
                            {
                                $level = $this->updateVipLevel((int)$userVip['vip_level'], $newStartNum, $vipModel);
                            } else
                            {
                                $level = $userVip['vip_level'];
                            }
                        }
                    }
                }

                $this->startTrans();
                try
                {
                    if ($level != $userVip['vip_level'])
                    {   // 用户等级变更
                        $levelRecord = [
                            'uid' => $uid,
                            'old_level' => $userVip['vip_level'],
                            'new_level' => $level,
                            'star_num' => $newStartNum,
                            'create_time' => time(),
                        ];
                        // 记录用户等级变更
                        (new ZlVipLevelUpgradeRecord())->save($levelRecord);
                    }

                    $userVipData = [
                        'star_num' => $newStartNum,
                        'vip_level' => $level,
                    ];// 更新用户星力值及等级信息
                    $this->save($userVipData, ['id' => $userVip['id']]);
                    $recordData = [
                        'uid' => $uid,
                        'type' => $type,
                        'flag' => $flag,
                        'star_num' => $starNum,
                        'create_time' => time(),
                    ];
                    // 添加成长值记录
                    $rid = $starNumRecordModel->insertGetId($recordData);
                    $this->commit();
                }
                catch (Exception $e)
                {   // 异常 不增加星力值
                    $this->rollback();

                    $errorData = [
                        'uid' => $uid,
                        'type' => $type,
                        'price' => $price,
                        'gid' => $gid,
                        'isSend' => $isSend,
                        'errorMsg' => $e->getMessage(),
                        'errorFile' => $e->getFile(),
                        'errorLine' => $e->getLine(),
                    ];
                    Log::write("星力值增加失败，错误消息" . var_export($errorData, true) . PHP_EOL, 'error', true);

                    return;
                }
                // 更新用户VIP信息
                CacheManage::getInstance()->setCacheConfig($this->cacheModel, $uid)->setCache($userVipData);
                // 发送升级消息
                if ($level > $userVip['vip_level'])
                {
                    $userPush = (new UserPush())->getUserRegid($uid);
                    if ($userPush && isset($userPush['version']) && $userPush['version'])
                    {
                        $userVersion = substr($userPush['version'], 1, strlen($userPush['version']));
                        $isNewBoon = Util::CheckAppVersion('2.10.5', $userVersion) !== '小';
                    } else
                    {
                        $isNewBoon = false;
                    }
                    $vipBoonMessage = (new ZlVipBoon())->getBoonMessageString($level, [], 1, $isNewBoon);
                    // 消息中心添加信息
                    $messageData = [
                        'status' => 0,
                        'is_read' => 0,
                        'range' => $uid,
                        'type' => 2,
                        'reid' => 0,
                        'cid' => 0,
                        'title' => '会员升级通知',
                        'content' => "恭喜您成功升级至【V{$level}】等级，解锁新权益：{$vipBoonMessage}，快去卡券中心查看并使用吧。",
                        'link' => 'vip_center?from=1&need_auth=2',
                        'target' => 'local',
                        'create_time' => time(),
                    ];
                    //发送消息
                    try
                    {
                        (new MessageCenter())->pushMessage($uid, $messageData);
                    }
                    catch (Exception $e)
                    {
                        print("会员 写入消息中心失败: " . $e->getMessage() . PHP_EOL);
                        Log::write("会员 写入消息中心失败: " . $e->getMessage() . PHP_EOL, 'error');
                    }
                    // 升级并自动赠送权益
                    if ($isSend)
                    {
                        (new ZlVipBoon())->vipBoonSend($uid);
                    }
                }
                // 记录用户任务完成情况
                if (in_array($type, [9, 14], false))
                    $starNumRecordModel->taskSuccess($uid, $type);
                // 添加成长明细缓存
                $starNumRecordModel->addRecordCache($uid, (int)$rid, $flag);
                //添加本月获取星力值总数
                $starNumRecordModel->updateMonthStarNum($uid, $monthStarNumTotal);

                // 神策打点 60 星力值变化 VipStartNumChange
                $sensorsTrackData = [
                    'star_num' => $starNum,// 星力值变化量
                    'task_name' => $taskInfo['record_name'],// 会员任务类型  每日幸运星领取、续费小行星会员……
                ];
                SensorsData::track($uid, true, 'VipStartNumChange', $sensorsTrackData);

                // 星力值变更时，设置用户星力值
                $userData = ['user_star_num' => $newStartNum];
                SensorsData::profile($uid, true, $userData, 'set');
            }
        }
        if ($isSend === true && $type == 8)
        {
            // 用户开通、激活会员时，更新用户属性
            $userData = ['user_is_vip' => true];
            SensorsData::profile($uid, true, $userData, 'set');
        }
    }

    /**
     * 升级用户VIP等级 升级时调用
     * @param int $level
     * @param int $starNum
     * @param ZlVip $vipModel
     * @return int
     * @throws \Exception
     */
    public function updateVipLevel(int $level, int $starNum, ZlVip $vipModel): int
    {
        $nextLevel = $level + 1;
        $nextLevelInfo = $vipModel->getInfoById($nextLevel);

        if ($starNum >= $nextLevelInfo['star_num'])
        {
            if ($nextLevel < 8)
            {
                return $this->updateVipLevel($nextLevel, $starNum, $vipModel);
            } else
            {
                return $nextLevel;
            }
        } else
        {
            return $level;
        }
    }

    /**
     * 从缓存中，获取最近20条，开通的vip用户id
     * @return array
     * @throws \Exception
     */
    public function getLatelyVipUsersFromCache()
    {
        $module = 'user.lately_vip_users';
        $data = CacheManage::getInstance()->setCacheConfig($module)->getCache([], 'all');
        if (!$data)
        {
            $data = $this->getLatelyVipUsersFromDb();
            if (!$data) return [];
            CacheManage::getInstance()->setCacheConfig($module)->setCache($data, 'all', true);
        }
        $data = is_array($data) ? $data : json_decode($data, true);

        return $data;
    }

    /**
     * 从数据库中，获取最近20条开通vip的用户
     * @return array
     */
    public function getLatelyVipUsersFromDb()
    {
        $field = 'uid as id';
        try
        {
            $data = $this->where('is_valid', 1)->field($field)->limit(20)->order('id DESC')->selectOrFail();
        }
        catch (\Exception $e)
        {
            $data = [];
        }
        return $data;
    }

    /**
     * 排行榜计算
     * @throws \think\exception\PDOException
     * @throws \Exception
     */
    public function vipRanking()
    {
        Log::write(date('Y-m-d H:i:s') . '排行榜统计开始', 'log');
        // 排行榜统计检查 判断今天是否统计过排行榜了
        $time = strtotime(date('Y-m-d 00:00:00'));
        if ((new ZlVipRanking())->where('create_time', $time)->count() > 0)
        {
            Log::write(date('Y-m-d H:i:s') . '排行榜统计结束;今天排行榜已经统计过了！', 'log');
            return;
        }
        $this->startTrans();
        try
        {
            $rankingData = $this->where('star_num', '>', 0)->order('star_num desc')->limit(200)->field('uid,star_num,vip_level')->select();

            $data = [];
            foreach ($rankingData as $key => $datum)
            {
                $data[] = [
                    'uid' => $datum['uid'],
                    'star_num' => $datum['star_num'],
                    'vip_level' => $datum['vip_level'],
                    'ranking' => $key + 1,
                    'create_time' => $time,
                ];
            }

            (new ZlVipRanking())->saveAll($data);

            CacheManage::getInstance()->setCacheConfig('vip.ranking', 'everyday')->rmCache('list');
            $this->commit();
        }
        catch (Exception $e)
        {
            $this->rollback();
            Log::write(date('Y-m-d H:i:s') . '排行榜统计异常；错误消息：' . $e->getMessage(), 'error');
            return;
        }
        Log::write(date('Y-m-d H:i:s') . '排行榜统计结束', 'log');
    }

    /**
     * 会员到期 自动扣减
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     * @throws \Exception
     */
    public function vipExpire()
    {
        Log::write(date('Y-m-d H:i:s') . '会员到期自动扣减开始', 'log');
        $expireVip = $this->where('is_valid', 0)->where('star_num', '>', 0)->field('uid')->select();

        foreach ($expireVip as $item)
        {
            $this->updateStarNum((int)$item['uid'], 14);
            usleep(10);
            Log::write(date('Y-m-d H:i:s') . "会员到期自动扣减完成，休息10毫秒；用户ID【{$item['uid']}】", 'log');
        }

        Log::write(date('Y-m-d H:i:s') . '会员到期自动扣减结束;到期扣减用户数量：' . count($expireVip), 'log');
    }

    /**
     * 获取VIP用户人数
     * @return int
     * @throws \Exception
     */
    public function vipNum(): int
    {
        $num = CacheManage::getInstance()->setCacheConfig('user.vip_total', '')->getDataAndSet(function ()
        {
            return $this->count();
        });

        return (int)$num;
    }

    /**
     * 内部vip重置
     * @param $uid
     * @param $data
     * @return array
     * @throws \Exception
     */
    private function innerVipReset($uid, $data)
    {
        if (isset($data['end_time']) && $data['end_time'] > time())
        {
            return $data;
        }
        $vipLevel = 0;
        $adminVipModel = new ZlAdminVip();
        $vip1 = $adminVipModel->getVipInfo(1);
        if (in_array($uid, $vip1, false))
        {
            $vipLevel = 1;
        } else
        {
            $vip2 = $adminVipModel->getVipInfo(2);
            if (in_array($uid, $vip2, false))
            {
                $vipLevel = 2;
            }
        }
        if ($vipLevel)
        {
            if ($data)
            {
                $data['end_time'] = strtotime('+1 month', time());
                $data['is_valid'] = 1;
            } else
            {
                $this->SaveData(['end_time' => 0, 'uid' => $uid, 'create_time' => time(), 'vip_level' => 1, 'is_valid' => 0]);
                $id = $this->getLastInsID();
                return ['id' => (int)$id, 'star_num' => 0, 'vip_level' => 1, 'is_valid' => 1, 'end_time' => strtotime('+1 month', time()), 'apple_status' => 1];
            }
        }
        return $data;
    }
}
